﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Timers;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Threading;
using SharpPcap;

namespace CNGIDemo
{
    /// <summary>
    /// Interaction logic for MainWindow.xaml
    /// </summary>
    public partial class MainWindow : Window
    {
        #region
        private int intervalTime = 400;//readTimeoutMilliseconds and Timer Interval
        public delegate void GetNextPacketDelegate();
        private int count = 0;
        private int captureMode;
        string ipv4SourceAddress;
        string ipv6SourceAddress;
        private ICaptureDevice device;
        private bool continueGetPacket;
        
        private int packetOfIpv4CountPerSecond;
        private int prepacketOfIpv4CountPerSecond;
        
        private int packetOfIpv6CountPerSecond;
        private int prepacketOfIpv6CountPerSecond;

        Timer timeRecord;
        ObservableCollection<ChartItem> Items = new ObservableCollection<ChartItem>();
        ObservableCollection<ChartItem> Items2 = new ObservableCollection<ChartItem>();

        System.Threading.Thread capturePacketThread;
        #endregion

        public MainWindow()
        {
            InitializeComponent();

            this.captureMode = 1;//default is p mode, 0 is normal mode. 1 is p mode
            this.continueGetPacket = false;
            intervalTime = 400;
            timeRecord = new System.Timers.Timer { Interval = intervalTime };//Milliseconds
            this.Loaded += ZoomIntoChartSample_Loaded;
        }

        /// Caching of the ChartArea template part.
        /// </summary>
        private Panel chartArea;

        /// <summary>
        /// Gets the ChartArea.
        /// </summary>
        /// <returns>TemplatePart ChartArea</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by ZoomChanged.")]
        private Panel ChartArea
        {
            get
            {
                if (chartArea == null)
                {
                    chartArea = GetLogicalChildrenBreadthFirst(ZoomChart).Where(element => element.Name.Equals("ChartArea")).FirstOrDefault() as Panel;
                }

                return chartArea;
            }
        }

        /// <summary>
        /// Caching of the ScrollArea template part.
        /// </summary>
        private ScrollViewer scrollArea;

        /// <summary>
        /// Gets the ScrollArea.
        /// </summary>
        /// <returns>TemplatePart ScrollArea</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by an event handler in XAML.")]
        private ScrollViewer ScrollArea
        {
            get
            {
                if (scrollArea == null)
                {
                    scrollArea = GetLogicalChildrenBreadthFirst(ZoomChart).Where(element => element.Name.Equals("ScrollArea")).FirstOrDefault() as ScrollViewer;
                }
                return scrollArea;
            }
        }

        /// <summary>
        /// Force an update of the chart.
        /// </summary>
        /// <param name="sender">The ZoomIntoChartSample instance.</param>
        /// <param name="e">Event arguments.</param>
        private void ZoomIntoChartSample_Loaded(object sender, RoutedEventArgs e)
        {
            var r = new Random();
           
            //Items.Add(new ChartItem(DateTime.Now, 10));
            //Items2.Add((new ChartItem(DateTime.Now, 5)));

            //occurs every intervalTime second
            timeRecord.Elapsed += (s, args) => Dispatcher.Invoke((Action)(() =>
            {
                ++count;
                //var currentTime = DateTime.Now.AddMilliseconds(count*this.intervalTime);
                Items.Add(new ChartItem(count, (this.packetOfIpv4CountPerSecond - this.prepacketOfIpv4CountPerSecond)));
                this.prepacketOfIpv4CountPerSecond = this.packetOfIpv4CountPerSecond;
                Items2.Add(new ChartItem(count, (packetOfIpv6CountPerSecond - this.prepacketOfIpv6CountPerSecond)));
                this.prepacketOfIpv6CountPerSecond = this.packetOfIpv6CountPerSecond;
            }));
            LineSeries1.ItemsSource = Items;
            LineSeries2.ItemsSource = Items2;

            // force synchronous layout pass
            ZoomChart.UpdateLayout();

            // and force initial zoom 
            UpdateChart(0);

            this.Start.IsEnabled = false;
            this.Stop.IsEnabled = false;
        }

        /// <summary>
        /// Handles the changing of the zoomlevel.
        /// </summary>
        /// <param name="sender">The zoom slider.</param>
        /// <param name="e">Event arguments.</param>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by an event handler in XAML.")]
        private void ZoomChanged(object sender, RoutedPropertyChangedEventArgs<double> e)
        {

            double zoom = e.NewValue;

            UpdateChart(zoom);
        }

        /// <summary>
        /// Updates the chart to zoom with the correct zoom factor.
        /// </summary>
        /// <param name="zoom">The percentage of zoom we wish to apply.</param>
        private void UpdateChart(double zoom)
        {
            ChartArea.Width = ScrollArea.ViewportWidth + (ScrollArea.ViewportWidth * zoom / 100.0);
        }

        /// <summary>
        /// Helper function that returns a list of the visual children.
        /// </summary>
        /// <param name="parent">Element whose visual children will be returned.</param>
        /// <returns>A collection of visualchildren.</returns>
        [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called by ChartArea and ScrollArea.")]
        private IEnumerable<FrameworkElement> GetLogicalChildrenBreadthFirst(FrameworkElement parent)
        {

            Queue<FrameworkElement> queue =
                new Queue<FrameworkElement>(GetVisualChildren(parent).OfType<FrameworkElement>());

            while (queue.Count > 0)
            {
                FrameworkElement element = queue.Dequeue();
                yield return element;

                foreach (FrameworkElement visualChild in GetVisualChildren(element).OfType<FrameworkElement>())
                {
                    queue.Enqueue(visualChild);
                }
            }
        }

        /// <summary>
        /// Helper function that returns the direct visual children of an element.
        /// </summary>
        /// <param name="parent">The element whose visual children will be returned.</param>
        /// <returns>A collection of visualchildren.</returns>
        private IEnumerable<DependencyObject> GetVisualChildren(DependencyObject parent)
        {
            int childCount = VisualTreeHelper.GetChildrenCount(parent);
            for (int counter = 0; counter < childCount; counter++)
            {
                yield return VisualTreeHelper.GetChild(parent, counter);
            }
        }

        private void FindInterfaces(object sender, RoutedEventArgs e)
        {
            InterfacesListView interfacesView = new InterfacesListView();
            interfacesView.Visibility = System.Windows.Visibility.Visible;
            interfacesView.ChooseInterfaceEvent += new InterfacesListView.ChooseInterfaceHandler(interfacesView_ChooseInterfaceEvent);
        }

        void interfacesView_ChooseInterfaceEvent(object sender, int interfaceIndex, int _captureMode)
        {
            /* Retrieve the device list */
            var devices = CaptureDeviceList.Instance;
            device = devices[interfaceIndex];
            this.captureMode = _captureMode;

            this.Start.IsEnabled = true;
        }

        private void SetFilter_Click(object sender, RoutedEventArgs e)
        {
            SetFilterUtil setFilterUtil = new SetFilterUtil(this.intervalTime.ToString());
            setFilterUtil.Visibility = System.Windows.Visibility.Visible;
            setFilterUtil.ApplyFilterRuleEvent += new SetFilterUtil.ApplyFilterRuleHandler(setFilterUtil_ApplyFilterRuleEvent);
        }

        void setFilterUtil_ApplyFilterRuleEvent(object sender,string _timeInterval, string _ipv4SourceAddress, string _ipv6SourceAddress)
        {
            this.ipv4SourceAddress = _ipv4SourceAddress;
            this.ipv6SourceAddress = _ipv6SourceAddress;
            this.intervalTime = Convert.ToInt32(_timeInterval);
        }

        private void Start_Click(object sender, RoutedEventArgs e)
        {
            if (device == null)
            {
                FindInterfaces(this, e);
                if (device == null)
                {
                    return;
                }
            }

            CaptureBegin();
        }

        private void Stop_Click(object sender, RoutedEventArgs e)
        {
            this.continueGetPacket = false;
            this.timeRecord.Stop();
            this.capturePacketThread.Abort();

            this.Stop.IsEnabled = false;
            this.Start.IsEnabled = true;
        }

        private void CaptureBegin()
        {

            // Open the device for capturing
            int readTimeoutMilliseconds = (intervalTime);
            if (captureMode == 1)
            {
                device.Open(DeviceMode.Promiscuous, readTimeoutMilliseconds);
            }
            else
            {
                device.Open(DeviceMode.Normal, readTimeoutMilliseconds);
            }

            //filter = "ip and tcp"
            if (ipv4SourceAddress != null && ipv4SourceAddress.CompareTo("") != 0 && ipv6SourceAddress!= null && ipv6SourceAddress.CompareTo("") != 0)
            {
                string tempstr = "(icmp or icmp6) && (src " + this.ipv4SourceAddress/*"10.21.5.144"*/+ " or src " + this.ipv6SourceAddress+")";
                device.Filter = tempstr;/*"2001:0:53aa:64c:1495:250f:601d:d4ce)"*/
            }
            else
            {
                device.Filter = "(icmp or icmp6)";
            }

            this.continueGetPacket = true;
            this.packetOfIpv4CountPerSecond = 0;
            this.packetOfIpv6CountPerSecond = 0;
            prepacketOfIpv4CountPerSecond = 0;
            prepacketOfIpv6CountPerSecond = 0;

            this.timeRecord.Start();
            var now = DateTime.Now;
            Items.Add(new ChartItem(0, 0));
            Items2.Add((new ChartItem(0, 0)));
            this.Stop.IsEnabled = true;
            this.Start.IsEnabled = false;

            //GetNextPacket();
            capturePacketThread = new System.Threading.Thread(new System.Threading.ThreadStart(DispatcherThread));
            capturePacketThread.Start();
        }

        public void DispatcherThread()
        {
            while (this.continueGetPacket)
            {
                GetNextPacket();
                //this.Dispatcher.BeginInvoke(DispatcherPriority.Normal, new GetNextPacketDelegate(GetNextPacket));
            }
        }

        public void GetNextPacket()
        {

            RawCapture packet;

            // Capture packets using GetNextPacket()
            if ((packet = device.GetNextPacket()) != null)
            {
                var currentPacket = PacketDotNet.Packet.ParsePacket(packet.LinkLayerType, packet.Data);
                var tempPacket = currentPacket;

                while (tempPacket != null)
                {
                    if (tempPacket is PacketDotNet.ICMPv4Packet)
                    {
                        ++this.packetOfIpv4CountPerSecond;
                        break;
                    }
                    else if (tempPacket is PacketDotNet.ICMPv6Packet)
                    {
                        ++this.packetOfIpv6CountPerSecond;
                        break;
                    }

                    tempPacket = tempPacket.PayloadPacket;
                }
            }
        }
    }
}
